<?php

namespace app\common\service;

use app\common\exception\BusinessException;
use PhpOffice\PhpSpreadsheet\Exception;
use PhpOffice\PhpSpreadsheet\IOFactory;

class ExcelDataToQuestionJsonService
{
    /**
     * excelKeyMap
     * @var string[]
     */
    private $excelKeyMap = [
        'questionType' => 'A',
        'questionBackground' => 'B',
        'questionAsk' => 'C',
        'answerOption.A' => 'D',
        'answerOption.B' => 'E',
        'answerOption.C' => 'F',
        'answerOption.D' => 'G',
        'answerOption.E' => 'H',
        'answerOption.F' => 'I',
        'answerOption.G' => 'J',
        'answerOption.H' => 'K',
        'correctOption' => 'L',
        'explanation' => 'M',
        'category_1' => 'N',
        'category_2' => 'O',
    ];

    private $questionTypeMap = [
        '单选题' => 1,
        '多选题' => 2,
        '判断题' => 3,
        '问答题' => 4,
        '填空题' => 5,
        '完形填空' => 6,
        '阅读理解' => 7,
        '名词解释' => 8,
    ];

    /**
     * checkOptionQuestionType
     * @var int[]
     */
    private $checkOptionQuestionType = [
        1,
        2,
        3
    ];

    public function getExcelKeyMap(): array
    {
        return $this->excelKeyMap;
    }

    public function setExcelKeyMap(array $excelKeyMap): void
    {
        $this->excelKeyMap = $excelKeyMap;
    }

    public function getQuestionTypeMap(): array
    {
        return $this->questionTypeMap;
    }

    public function setQuestionTypeMap(array $questionTypeMap): void
    {
        $this->questionTypeMap = $questionTypeMap;
    }

    /**
     * invoke
     * @param $attachment
     * @return array
     * @throws Exception
     * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception|BusinessException
     */
    public function invoke($attachment)
    {
        // 提取参数
        $attachment = attachmentRealPath($attachment);
        if (!file_exists($attachment)) {
            throw new BusinessException('文件' . $attachment . '不存在');
        }

        // 加载文件
        $reader = IOFactory::createReader('Xlsx');
        $reader->setReadDataOnly(true);
        $spreadsheet = $reader->load($attachment);

        // 读取内容
        $sheetData = $spreadsheet->getSheet(0)->toArray(null, true, true, true);
        // 循环检查数据
        $allData = [];
        $allError = [];
        foreach ($sheetData as $key => $val) {
            if ($key == 1) {
                continue;
            }
            // 跳过空行
            $val = array_map('trim', $val); // 去除首尾空格
            if (empty(array_filter($val))) {
                continue;
            }
            try {
                $allData[] = $this->checkAndConvertItem($val);
            } catch (\Exception $exception) {
                $error = '第A' . $key . '行,此题需要检查,' . $exception->getMessage();
                $allError[] = $error;
            }
        }
        if ($allError) {
            throw new BusinessException(join(',', $allError));
        }
        return $allData;
    }

    /**
     * checkAndConvertItem
     * @throws BusinessException
     */
    private function checkAndConvertItem($item)
    {
        // 检查表头并转换数据
        $newItem = [];
        foreach ($this->excelKeyMap as $key => $val) {
            if (!array_key_exists($val, $item)) {
                throw new BusinessException('表头' . $val . '数据异常');
            }
            if (strpos($key, '.') !== false) {
                $key = $key[strlen($key) - 1];
                $newItem['answerOption'][$key] = trim($item[$val]);
            } else {
                $newItem[$key] = trim($item[$val]);
            }
        }

        // 检查必填字段
        if (empty($newItem['questionType'])) {
            throw new BusinessException('题型单元格数据必须填写');
        }
        if (empty($newItem['questionAsk'])) {
            throw new BusinessException('题目内容单元格数据必须填写');
        }

        // 转换并检查questionType
        $newQuestionType = $this->questionTypeMap[$newItem['questionType']] ?? '';
        if (!$newQuestionType) {
            throw new BusinessException('填写的题型"' . $newItem['questionType'] . '"不正确,请使用模板中的题型.');
        }
        $newItem['questionType'] = $newQuestionType;


        // 根据questionType检查必填选项和检查答案是否正确
        if (in_array($newQuestionType, $this->checkOptionQuestionType)) {


            // 单选多选判断,必须要有选择
            if (empty($newItem['answerOption'])) {
                throw new BusinessException('题型"' . $newItem['questionType'] . '"需要填写题目选项信息');
            }

            // 如果是判断题必须要有A和B
            if ($newQuestionType == 3) {
                if (empty($newItem['answerOption']['A']) || empty($newItem['answerOption']['B'])) {
                    throw new BusinessException('判断题必须要有2个选项,例如A.正确 B.错误');
                }
            }

            // 检查答案是否正确填写
            $newItem['correctOption'] = preg_replace('/[^a-zA-Z]/', '', $newItem['correctOption']);
            $newItem['correctOption'] = strtoupper($newItem['correctOption']);
            if (!preg_match('/^[A-H]+$/', $newItem['correctOption'])) {
                throw new BusinessException('题目答案必须是选项的字母,例如ABCDEFGH');
            }
        }

        // 检查必须有题目答案
        if (empty($newItem['correctOption'])) {
            throw new BusinessException('请填写正确答案');
        }

        // 返回
        return $newItem;
    }
}